package cn.pku.eecs.ldn.view.listview;

import java.util.Calendar;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.ListView;
import android.widget.Scroller;

public class PullUpAndDownListView extends ListView {
	private static final String TAG = "PullUpAndDownListView";
	private static final boolean DEBUG = true;

	private final int SCROLL_QUOTIENT = 2;
	private final int SCROLL_DURATION = 500; // in ms

	private Scroller mScroller;

	private final int SCROLL_DST_TOP = 0; // scroll back to top
	private final int SCROLL_DST_BOTTOM = 1; // scroll back to bottom
	private int scrollDst = SCROLL_DST_TOP;

	// -- header view
	private HeaderView mHeaderView;
	private int headViewContentHeight;

	private boolean isRefreshing = false;
	private boolean isPullDownRefreshingEnabled = false;

	private OnPullListener onPullListener = null;

	public PullUpAndDownListView(Context context) {
		super(context);
		init(context);
	}

	public PullUpAndDownListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public PullUpAndDownListView(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	private void init(Context context) {
		mScroller = new Scroller(context, new DecelerateInterpolator());

		mHeaderView = new HeaderView(context);

		addHeaderView(mHeaderView);

		// setListViewBehaviorType(TYPE_PULL_EMPTY);
		headViewContentHeight = mHeaderView.getContentViewHeight();
		setPullDownRefreshingEnabled(isPullDownRefreshingEnabled);
	}

	private void updateHeaderHeight(float delta) {
		mHeaderView.setHeight((int) delta + mHeaderView.getHeight());
		if (isPullDownRefreshingEnabled) {
			if (!isRefreshing) {
				if (mHeaderView.getHeight() > headViewContentHeight) {
					mHeaderView.setState(HeaderView.STATE_READY);
				} else {
					mHeaderView.setState(HeaderView.STATE_NORMAL);
				}
			}
		}

		setSelection(0); // scroll to top each time
	}

	/**
	 * reset header view's height. ͨ��mScroller��ʵ�ֹ��Ч��
	 */
	private void resetHeaderHeight() {
		if (DEBUG)
			Log.d(TAG, "resetHeaderHeight()");
		int height = mHeaderView.getHeight();
		if (height == 0) {
			return;
		}
		int finalHeight = 0;
		if (isPullDownRefreshingEnabled) {
			if (isRefreshing) {
				if (height > headViewContentHeight) {
					finalHeight = headViewContentHeight;
				} else {
					finalHeight = height; // remain the same when refreshing
				}
			}
		}
		if (finalHeight == height) {
			return;
		}
		scrollDst = SCROLL_DST_TOP;
		mScroller.startScroll(0, height, 0, finalHeight - height,
				SCROLL_DURATION);
		invalidate();
	}

	float mLastY = 0;

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if (mLastY == -1) {
			mLastY = ev.getRawY();
		}

		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mLastY = ev.getRawY();
			break;

		case MotionEvent.ACTION_MOVE:
			final float deltaY = ev.getRawY() - mLastY;
			mLastY = ev.getRawY();
			if (getFirstVisiblePosition() == 0
					&& (mHeaderView.getHeight() > 0 || deltaY > 0)) {
				updateHeaderHeight(deltaY / SCROLL_QUOTIENT);
			}
			break;
		default:
			mLastY = -1;
			if (getFirstVisiblePosition() == 0) {
				if (isPullDownRefreshingEnabled
						&& mHeaderView.getHeight() > headViewContentHeight) {
					startRefreshing();
				} else {
					if (DEBUG)
						Log.i(TAG, "onTouchEvent(): do nothing");
				}
				resetHeaderHeight();
			}
			break;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * update header or footer view's height when the scroll is in animation
	 */
	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			// if (DEBUG)
			// Log.i(TAG, "computeScroll(): mScroller " + mScroller.getCurrY());
			if (scrollDst == SCROLL_DST_TOP) {
				mHeaderView.setHeight(mScroller.getCurrY());
			}
			invalidate();
		}
		super.computeScroll();
	}

	public void setPullDownRefreshingEnabled(boolean enable) {
		if (DEBUG)
			Log.d(TAG, "setPullDownRefreshingEnabled(): " + enable);
		isPullDownRefreshingEnabled = enable;
		if (isPullDownRefreshingEnabled) {
			mHeaderView.setState(HeaderView.STATE_NORMAL);
		} else {
			mHeaderView.setState(HeaderView.STATE_EMPTY);
		}
	}

	public void setOnPullListener(OnPullListener onPullListener) {
		this.onPullListener = onPullListener;
	}

	public void startRefreshing() {
		if (DEBUG)
			Log.d(TAG, "startRefreshing()");
		if (isRefreshing) {
			if (DEBUG)
				Log.i(TAG, "startRefreshing(): last refresh still going on");
			return;
		}
		isRefreshing = true;
		mHeaderView.setState(HeaderView.STATE_REFRESHING);
		if (onPullListener != null) {
			onPullListener.onRefresh();
		} else {
			if (DEBUG)
				Log.e(TAG, "startRefreshing(): onPullListener is null");
		}
	}

	/**
	 * this method will also update lastUpdatedTime so u don't have to explictly
	 * call setLastUpdatedTime(...).
	 */
	public void stopRefreshing() {
		if (isRefreshing == false) {
			return;
		}
		isRefreshing = false;
		mHeaderView.setState(HeaderView.STATE_NORMAL);
		resetHeaderHeight();
		setLastUpdatedTimeAsCurrent();
	}

	private void setLastUpdatedTimeAsCurrent() {
		if (mHeaderView == null) {
			if (DEBUG)
				Log.e(TAG, "setLastUpdatedTimeAsCurrent(): mHeaderView is null");
			return;
		}
		Calendar calendar = Calendar.getInstance();
		mHeaderView.setLastUpdatedTime(generateTimeString(calendar
				.getTimeInMillis()));
	}

	/**
	 * u don't have to call the method after refresh if stopRefreshing() is
	 * called. Usually u should call this method when the list view is first
	 * created.
	 * 
	 * @param milliseconds
	 */
	public void setLastUpdatedTime(long milliseconds) {
		if (mHeaderView == null) {
			if (DEBUG)
				Log.e(TAG, "setLastUpdatedTime(): mHeaderView is null");
			return;
		}
		mHeaderView.setLastUpdatedTime(generateTimeString(milliseconds));
	}

	private String generateTimeString(long time) {
		String ret = "δ֪";
		Calendar calendar = Calendar.getInstance();
		long timeCurrent = calendar.getTimeInMillis();
		if (timeCurrent <= time) {
			ret = "�ո�";
		} else {
			long timePast = (timeCurrent - time) / 1000; // in second
			if (timePast < 1) {
				ret = "�ո�";
			} else if (timePast <= 60) {
				ret = timePast + "����ǰ";
			} else if (timePast <= 3600) {
				ret = timePast / 60 + "����ǰ";
			} else if (timePast <= 3600 * 24) {
				ret = timePast / 3600 + "Сʱǰ";
			} else {
				int curYear = calendar.get(Calendar.YEAR);
				calendar.setTimeInMillis(time);
				int year = calendar.get(Calendar.YEAR);
				int month = calendar.get(Calendar.MONTH);
				int day = calendar.get(Calendar.DAY_OF_MONTH);
				if (year == curYear) {
					ret = month + "��" + day + "��";
				} else if (year < curYear) {
					ret = year + "��" + month + "��";
				}
			}
		}
		return ret;
	}

	public void stopLoadingMore() {

	}

	public interface OnPullListener {
		public void onRefresh();

		public void onLoadMore();
	}
}
